package l2hc.gameserver.ai;

import static l2hc.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import static l2hc.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;

import java.util.concurrent.ScheduledFuture;

import l2hc.commons.threading.RunnableImpl;
import l2hc.gameserver.ThreadPoolManager;
import l2hc.gameserver.geodata.GeoEngine;
import l2hc.gameserver.model.Creature;
import l2hc.gameserver.model.FakePlayer;
import l2hc.gameserver.model.Player;

/**
 * @author ALF
 * @date 12.07.2012
 */
public class FakePlayerAI extends CharacterAI
{
	private volatile int thinking = 0; // Что бы небыло рекурсии
	private Object _intention_arg0 = null, _intention_arg1 = null;
	private ScheduledFuture<?> _followTask;

	public FakePlayerAI(FakePlayer actor)
	{
		super(actor);
	}

	@Override
	public void changeIntention(CtrlIntention intention, Object arg0, Object arg1)
	{
		super.changeIntention(intention, arg0, arg1);
		_intention_arg0 = arg0;
		_intention_arg1 = arg1;
	}

	@Override
	public void setIntention(CtrlIntention intention, Object arg0, Object arg1)
	{
		_intention_arg0 = null;
		_intention_arg1 = null;
		super.setIntention(intention, arg0, arg1);
	}

	@SuppressWarnings("incomplete-switch")
	@Override
	protected final void onEvtThink()
	{
		FakePlayer actor = getActor();
		if (actor.isActionsDisabled())
			return;

		try
		{
			if (thinking++ > 1)
				return;

			switch (getIntention())
			{
				case AI_INTENTION_ACTIVE:
					// thinkActive();
					break;
				case AI_INTENTION_ATTACK:
					thinkAttack(true);
					break;
				case AI_INTENTION_FOLLOW:
					thinkFollow();
					break;
			}
		}
		catch (Exception e)
		{
			_log.error("", e);
		}
		finally
		{
			thinking--;
		}
	}

	protected void thinkFollow()
	{
		FakePlayer actor = getActor();

		Creature target = (Creature) _intention_arg0;
		Integer offset = (Integer) _intention_arg1;

		// Находимся слишком далеко цели, либо цель не пригодна для следования
		if (target == null || target.isAlikeDead() || actor.getDistance(target) > 4000 || offset == null)
		{
			clientActionFailed();
			return;
		}

		// Уже следуем за этой целью
		if (actor.isFollow && actor.getFollowTarget() == target)
		{
			clientActionFailed();
			return;
		}

		// Находимся достаточно близко или не можем двигаться - побежим потом ?
		if (actor.isInRange(target, offset + 20) || actor.isMovementDisabled())
			clientActionFailed();

		if (_followTask != null)
		{
			_followTask.cancel(false);
			_followTask = null;
		}

		_followTask = ThreadPoolManager.getInstance().schedule(new ThinkFollow(), 250L);
	}

	protected class ThinkFollow extends RunnableImpl
	{
		@Override
		public void runImpl() throws Exception
		{
			FakePlayer actor = getActor();

			if (getIntention() != AI_INTENTION_FOLLOW)
				return;

			Creature target = (Creature) _intention_arg0;
			int offset = _intention_arg1 instanceof Integer ? (Integer) _intention_arg1 : 0;

			if (target == null || target.isAlikeDead() || actor.getDistance(target) > 4000)
			{
				setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
				return;
			}

			Player player = actor.getPlayer();
			if (player == null || player.isLogoutStarted())
			{
				setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
				return;
			}

			if (!actor.isInRange(target, offset + 20) && (!actor.isFollow || actor.getFollowTarget() != target))
				actor.followToCharacter(target, offset, false);
			_followTask = ThreadPoolManager.getInstance().schedule(this, 250L);
		}
	}

	protected void thinkAttack(boolean checkRange)
	{
		FakePlayer actor = getActor();

		Player player = actor.getPlayer();
		if (player == null)
		{
			setIntention(AI_INTENTION_ACTIVE);
			return;
		}

		if (actor.isActionsDisabled() || actor.isAttackingDisabled())
		{
			return;
		}

		Creature attack_target = getAttackTarget();
		if (attack_target == null || attack_target.isDead())
		{
			setIntention(AI_INTENTION_FOLLOW);
			return;
		}

		if (!checkRange)
		{
			clientStopMoving();
			actor.doAttack(attack_target);
			return;
		}

		int range = actor.getPhysicalAttackRange();
		if (range < 10)
			range = 10;

		boolean canSee = GeoEngine.canSeeTarget(actor, attack_target, false);

		if (!canSee && (range > 200 || Math.abs(actor.getZ() - attack_target.getZ()) > 200))
		{
			setIntention(AI_INTENTION_FOLLOW);
			return;
		}

		range += actor.getMinDistance(attack_target);

		if (actor.isFakeDeath())
			actor.breakFakeDeath();

		if (actor.isInRangeZ(attack_target, range))
		{
			if (!canSee)
			{
				setIntention(AI_INTENTION_FOLLOW);
				return;
			}

			clientStopMoving(false);
			actor.doAttack(attack_target);
		}
	}

	@Override
	public FakePlayer getActor()
	{
		return (FakePlayer) super.getActor();
	}

}
